package com.valuegroup.smarttodo.data.service;

import android.text.TextUtils;

import com.alibaba.fastjson.JSON;
import com.valuegroup.smarttodo.data.ServiceFactory;
import com.valuegroup.smarttodo.data.model.AppStatus;
import com.valuegroup.smarttodo.data.model.Curriculum;
import com.valuegroup.smarttodo.data.model.Event;
import com.valuegroup.smarttodo.data.model.EventDao;
import com.valuegroup.smarttodo.data.model.HabitEvent;
import com.valuegroup.smarttodo.data.model.HabitEventDao;
import com.valuegroup.smarttodo.data.model.Task;
import com.valuegroup.smarttodo.data.model.TaskDao;
import com.valuegroup.smarttodo.data.timeline.OrderStatus;
import com.valuegroup.smarttodo.data.vo.EventStatisticsVo;
import com.valuegroup.smarttodo.data.vo.CalendarQueryParams;
import com.valuegroup.smarttodo.data.vo.WeekPlanEventVo;
import com.valuegroup.smarttodo.data.vo.WeekPlanQueryParams;
import com.valuegroup.smarttodo.data.vo.WeekPlanVo;
import com.valuegroup.smarttodo.data.vo.WeekProgressVo;
import com.valuegroup.smarttodo.plugins.pickdialog.DateUtils;

import org.greenrobot.greendao.query.QueryBuilder;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;

public class EventService extends BaseService {

    public List<WeekPlanEventVo> findEvent(CalendarQueryParams p) {

        List<Event> events = new ArrayList<>();
        if (!TextUtils.isEmpty(p.getSearchKey())) {
            // 查询关键字（查询所有）
            events.addAll(searchEvent(p.getSearchKey()));
        } else {
            if (DateUtils.compareDate(p.getFindDay(), new Date()) < 0) {
                // 查询日期为已过去的日期时,只能查询已选的
                events.addAll(findDayEvent(p.getFindDay()));
            } else {
                // 0 全部；1已选；2未选
                if (p.getFilterType() == 2) {
                    events.addAll(findUnJoinEvent(p.getFindDay()));
                } else if (p.getFilterType() == 1) {
                    events.addAll(findDayEvent(p.getFindDay()));
                } else {
                    // 查询指定日期
                    events.addAll(findDayEvent(p.getFindDay()));
                    // 所有未指定事件
                    events.addAll(findUnJoinEvent(p.getFindDay()));
                }
            }
        }

        List<WeekPlanEventVo> result = new ArrayList<>();
        events.forEach(n -> {
            WeekPlanEventVo v = new WeekPlanEventVo();

            v.setId(n.getId());
            v.setTitle(n.getTitle());
            v.setSourceId(n.getSourceId());
            v.setEventType(n.getEventType());
            v.setPreMinute(n.getPreMinute());
            v.setJoinSchedule(n.getJoinSchedule());
            v.setJoinScheduleDt(n.getJoinScheduleDt());
            v.setScheduleWeekNum(n.getScheduleWeekNum());
            v.setScheduleDt(n.getScheduleDt());
            v.setFinish(n.getFinish());
            v.setFinishDt(n.getFinishDt());
            v.setCreateDt(n.getCreatedDt());

            result.add(v);
        });

        return result;
    }

    public List<Event> findDayEvent(Date selectDay) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        // 已加入排期
        q.where(EventDao.Properties.JoinSchedule.eq(true));
        // 且在指定排期日
        q.where(EventDao.Properties.ScheduleDt.ge(DateUtils.setStartTime(selectDay)));
        q.where(EventDao.Properties.ScheduleDt.le(DateUtils.setEndTime(selectDay)));
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        // 按事件类型排序
        q.orderAsc(EventDao.Properties.EventType);
        q.orderAsc(EventDao.Properties.SourceId);
        return q.list();
    }


    public List<Event> searchEvent(String searchKey) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        if (!TextUtils.isEmpty(searchKey)) {
            q.where(EventDao.Properties.Title.like("%" + searchKey + "%"));
        }
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));

        q.orderAsc(EventDao.Properties.EventType);
        q.orderAsc(EventDao.Properties.SourceId);
        return q.list();
    }

    public List<Event> findUnJoinEvent(Date selectDay) {
        List<Event> result = new ArrayList<>();

        // 按时间维度分别统计各类未排期事件
        result.addAll(findUnJoinEventForCurriculum());
        result.addAll(findUnJoinEventForCurrentMonth(selectDay));
        result.addAll(findUnJoinEventForCurrentQuarter(selectDay));
        result.addAll(findUnJoinEventForCurrentYear(selectDay));

        return result;
    }

    public List<Event> findUnJoinEventForCurriculum() {
        // 月度未排期事件
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.eq(AppStatus.EventType.STUDY));
        // 非自动续学类任务
        q.where(EventDao.Properties.AutoContinue.eq(false));
        q.where(EventDao.Properties.JoinSchedule.eq(false));
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return q.list();
    }

    public List<Event> findUnJoinEventForCurrentMonth(Date selectDay) {
        // 月度未排期事件
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.eq(AppStatus.EventType.GOALS_MONTH));
        q.where(EventDao.Properties.SourceMonth.eq(DateUtils.getMoth(selectDay)));
        q.where(EventDao.Properties.SourceYear.eq(DateUtils.getYear(selectDay)));
        q.where(EventDao.Properties.JoinSchedule.eq(false));
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return q.list();
    }

    public List<Event> findUnJoinEventForCurrentQuarter(Date selectDay) {
        // 季度度未排期事件
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.eq(AppStatus.EventType.GOALS_QUARTER));
        q.where(EventDao.Properties.SourceQuarter.eq(DateUtils.getQuarter(selectDay)));
        q.where(EventDao.Properties.SourceYear.eq(DateUtils.getYear(selectDay)));
        q.where(EventDao.Properties.JoinSchedule.eq(false));
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return q.list();
    }

    public List<Event> findUnJoinEventForCurrentYear(Date selectDay) {
        // 年度度未排期事件
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.eq(AppStatus.EventType.GOALS_YEAR));
        q.where(EventDao.Properties.SourceYear.eq(DateUtils.getYear(selectDay)));
        q.where(EventDao.Properties.JoinSchedule.eq(false));
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return q.list();
    }


    public void doRmoveJoinScheduleDelTask(Long id) {
        // 移除事件子任务
        Event e = getEventDao().load(id);
        if (e != null) {
            e.setJoinSchedule(false);
            e.setJoinScheduleDt(null);
            e.setScheduleDt(null);
            e.setScheduleWeekNum(0);
            e.setFinish(false);
            e.setFinishDt(null);
            e.setUpdatedDt(new Date());
            getEventDao().save(e);

            // 移除对应的任务
            delEventTask(e);
        }
    }

    public void doJoinScheduleAndCreateTask(Long eventId, Date findDay) {
        // 创建事件子任务
        Event e = getEventDao().load(eventId);
        if (e != null) {
            if (eventIsJoinTask(eventId)) {
                return;// 存在，避免重复添加
            }
            e.setJoinSchedule(true);
            e.setJoinScheduleDt(new Date());
            e.setScheduleDt(findDay);
            // 201901 (年+第几周)
            e.setScheduleWeekNum(DateUtils.getYearWeekNumber(findDay));
            e.setUpdatedDt(new Date());
            getEventDao().save(e);

            // 创建对应的任务
            createEventTask(e);
        }
    }

    public void doCreateTaskAndSetFinish(Long eventId, Boolean isFinish, Date finishDt) {

        // 创建事件子任务(存在则不创建)
        doJoinScheduleAndCreateTask(eventId, finishDt);

        // 将事件和事件子任务标记为完成状态
        Task task = findEventTask(eventId);
        if (task != null) {
            ServiceFactory.getInstance().getTaskService().doFinish(task.getId(), isFinish, finishDt);
        }
    }

    public void delEventTask(Event e) {
        List<Task> tasks = getTaskDao().queryBuilder()
                .where(TaskDao.Properties.SourceId.eq(e.getId()))
                .where(TaskDao.Properties.Status.eq(AppStatus.Status.NORMAL))
                .list();

        if (tasks != null && tasks.size() > 0) {
            getTaskDao().deleteInTx(tasks);
        }
    }

    public boolean eventIsJoinTask(long eventId) {
        // 查找事件是否存在任务信息
        long count = getTaskDao().queryBuilder()
                .where(TaskDao.Properties.SourceId.eq(eventId))
                .where(TaskDao.Properties.Status.eq(AppStatus.Status.NORMAL))
                .count();
        return count > 0;
    }

    public Task findEventTask(long eventId) {
        // 查找事件的子任务信息
        List<Task> tasks = getTaskDao().queryBuilder()
                .where(TaskDao.Properties.SourceId.eq(eventId))
                .where(TaskDao.Properties.Status.eq(AppStatus.Status.NORMAL))
                .list();

        if (tasks != null && tasks.size() > 0) {
            return tasks.get(0);
        }
        return null;
    }

    private void createEventTask(Event e) {
        Task t = ServiceFactory.getInstance().getTaskService().getDefaultTaskData();
        t.setTitle(e.getTitle());
        t.setPreMinute(e.getPreMinute()); // 番茄时间
        t.setDeadline(e.getScheduleDt());

        // 关联关系
        t.setSourceId(e.getId());
        switch (e.getEventType()) {
            case AppStatus.EventType.GOALS_MONTH:
            case AppStatus.EventType.GOALS_QUARTER:
            case AppStatus.EventType.GOALS_YEAR:
                // 目标任务
                t.setTaskType(AppStatus.TaskType.GOAL);
                break;
            case AppStatus.EventType.STUDY:
                // 学习任务
                t.setTaskType(AppStatus.TaskType.STUDY);
                break;
        }
        getTaskDao().save(t);
    }

    public EventStatisticsVo countAllEvent(int weekNum) {
        EventStatisticsVo v = new EventStatisticsVo();

        v.setStudyTotal(countCurriculumEvent(weekNum, false));
        v.setStudyFinish(countCurriculumEvent(weekNum, true));
        v.setGoalTotal(countGoalEvent(weekNum, false));
        v.setGoalFinish(countGoalEvent(weekNum, true));
        v.setHabitTotal(countHabitEvent(weekNum, false));
        v.setHabitFinish(countHabitEvent(weekNum, true));
        v.setEventTotal(v.getGoalTotal() + v.getHabitTotal() + v.getStudyTotal());
        v.setEventFinish(v.getGoalFinish() + v.getHabitFinish() + v.getStudyFinish());

        v.setPoolSchedu(countEventPool(true));
        v.setPoolTotal(countEventPool(false));
        float progress = (float) v.getPoolSchedu() / v.getPoolTotal() * 100;
        v.setPoolPercent(progress);

        return v;
    }

    private int countHabitEvent(int weekNum, boolean onlyFinish) {
        QueryBuilder<HabitEvent> q = getHabitEventDao().queryBuilder();
        q.where(HabitEventDao.Properties.ScheduleWeekNum.eq(weekNum));
        if (onlyFinish) {
            q.where(HabitEventDao.Properties.Finish.eq(true));
        }
        return (int) q.count();
    }


    private int countGoalEvent(int weekNum, boolean onlyFinish) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.gt(AppStatus.EventType.LINE_GOALS));
        q.where(EventDao.Properties.EventType.lt(AppStatus.EventType.LINE_GOALS + 10));
        q.where(EventDao.Properties.ScheduleWeekNum.eq(weekNum));
        q.where(EventDao.Properties.JoinSchedule.eq(true));
        if (onlyFinish) {
            q.where(EventDao.Properties.Finish.eq(true));
        }
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return (int) q.count();
    }

    private int countCurriculumEvent(int weekNum, boolean onlyFinish) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.gt(AppStatus.EventType.LINE_STUDY));
        q.where(EventDao.Properties.EventType.lt(AppStatus.EventType.LINE_STUDY + 10));
        q.where(EventDao.Properties.ScheduleWeekNum.eq(weekNum));
        q.where(EventDao.Properties.JoinSchedule.eq(true));
        if (onlyFinish) {
            q.where(EventDao.Properties.Finish.eq(true));
        }
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return (int) q.count();
    }

    private int countEventPool(boolean onlySchedu) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        if (onlySchedu) {
            q.where(EventDao.Properties.JoinSchedule.eq(true));
        }
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return (int) q.count();
    }

    public LinkedHashMap<String, List<WeekPlanVo>> findWeekPlan(WeekPlanQueryParams queryParams) {
        LinkedHashMap<String, List<WeekPlanVo>> map = new LinkedHashMap<>();

        switch (queryParams.getRange()) {
            case 0:
                // 近三周
                Date last7today = DateUtils.moveDay(new Date(), -7);
                Date today = new Date();
                Date next7today = DateUtils.moveDay(new Date(), +7);

                map.put(getFormatKey(last7today),
                        findWeekEventForWeekPlan(DateUtils.getYearWeekNumber(last7today)));
                map.put(getFormatKey(today),
                        findWeekEventForWeekPlan(DateUtils.getYearWeekNumber(today)));
                map.put(getFormatKey(next7today),
                        findWeekEventForWeekPlan(DateUtils.getYearWeekNumber(next7today)));
                break;
            case 1:
                // 周
                map.put(getFormatKey(queryParams.getYear(), queryParams.getWeek()),
                        findWeekEventForWeekPlan(queryParams.getYear() * 100 + queryParams.getWeek()));
                break;
            case 2:
                // 年
                HashMap<Integer, List<WeekPlanVo>> tempMap = findYearEventAndGroupByWeek(queryParams.getYear());
                if (tempMap != null && tempMap.size() > 0) {

                    Set<Integer> sortSet = new TreeSet<Integer>(new Comparator<Integer>() {
                        @Override
                        public int compare(Integer o1, Integer o2) {
                            return queryParams.isYearOrderAsc() ? o1.compareTo(o2) : o2.compareTo(o1);
                        }
                    });
                    sortSet.addAll(tempMap.keySet());

                    for (Integer key : sortSet) {
                        map.put(getFormatKey(key), tempMap.get(key));
                    }
                }
                break;
        }

        return map;
    }


    public HashMap<Integer, List<WeekPlanVo>> findYearEventAndGroupByWeek(int year) {
        HashMap<Integer, List<WeekPlanVo>> tempMap = new HashMap<>();

        // 查询全年已安排的事件
        List<Event> events = findYearEvent(year);
        if (events != null && events.size() > 0) {
            for (int i = 0; i < events.size(); i++) {

                Event e = events.get(i);
                Integer key = e.getScheduleWeekNum();

                // 按周分组
                if (tempMap.containsKey(key)) {
                    // 转换成VO
                    tempMap.get(key).add(converWeekPlanVo(e));
                } else {
                    tempMap.put(key, new ArrayList<>());
                    tempMap.get(key).add(converWeekPlanVo(e));
                }
            }

        }

        return tempMap;
    }


    private String getFormatKey(Date day) {
        return getFormatKey(DateUtils.getYear(day), DateUtils.getWeekOfYear(day));
    }

    private String getFormatKey(int year, int week) {
        return String.format(Locale.CHINESE, "%d年第%d周", year, week);
    }

    private String getFormatKey(int weekNum) {
        int year = Integer.parseInt(String.valueOf(weekNum).substring(0, 4));
        int weekNo = Integer.parseInt(String.valueOf(weekNum).substring(4));
        return getFormatKey(year, weekNo);
    }


    public List<WeekPlanVo> findYearEventForWeekPlan(int year) {
        List<WeekPlanVo> result = new ArrayList<>();
        List<Event> events = findYearEvent(year);
        if (events != null && events.size() > 0) {
            for (int i = 0; i < events.size(); i++) {
                WeekPlanVo vo = converWeekPlanVo(events.get(i));
                result.add(vo);
            }
        }
        return result;
    }

    public List<WeekPlanVo> findWeekEventForWeekPlan(int weekNum) {
        List<WeekPlanVo> result = new ArrayList<>();
        List<Event> events = findWeekEvent(weekNum);
        if (events != null && events.size() > 0) {
            for (int i = 0; i < events.size(); i++) {
                result.add(converWeekPlanVo(events.get(i)));
            }
        }
        return result;
    }

    private WeekPlanVo converWeekPlanVo(Event e) {
        WeekPlanVo vo = new WeekPlanVo();
        vo.setId(e.getId());
        vo.setTitle(e.getTitle());
        vo.setDate(e.getScheduleDt());
        vo.setDateStr(DateUtils.date2str(e.getScheduleDt(), DateUtils.F34));
        vo.setWeekNum(DateUtils.getWeek(e.getScheduleDt()));
        vo.setWeek(DateUtils.getWeek2Str(e.getScheduleDt()).replace("星期", "周"));
        vo.setMinute(e.getPreMinute());
        vo.setStatus(e.getFinish() ? OrderStatus.ACTIVE : OrderStatus.INACTIVE);
        vo.setEventType(e.getEventType());
        vo.setFinish(e.getFinish());
        return vo;
    }

    public List<Event> findYearEvent(int year) {
        year = year * 100;
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        //  >= 201900
        q.where(EventDao.Properties.ScheduleWeekNum.ge(year));
        //  < 202000 (201900+100)
        q.where(EventDao.Properties.ScheduleWeekNum.lt(year + 100));
        q.where(EventDao.Properties.JoinSchedule.eq(true));
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        q.orderAsc(EventDao.Properties.ScheduleDt);
        q.orderAsc(EventDao.Properties.ScheduleDt);
        return q.list();
    }

    public List<Event> findWeekEvent(int weekNum) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.ScheduleWeekNum.eq(weekNum));
        q.where(EventDao.Properties.JoinSchedule.eq(true));
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        q.orderAsc(EventDao.Properties.ScheduleDt);
        return q.list();
    }

    public List<WeekProgressVo> countWeekProgressData(int weekNum) {
        List<WeekProgressVo> result = new ArrayList<>();

        List<Date> weekDays = DateUtils.getDayListForWeekNum(String.valueOf(weekNum));
        System.out.println(">>>>>>>>>>>统计" + weekNum + "周内容" + JSON.toJSONString(weekDays));

        for (int i = 0; i < weekDays.size(); i++) {
            Date day = weekDays.get(i);

            WeekProgressVo v = new WeekProgressVo();
            v.setLabDate(DateUtils.date2str(day, DateUtils.F32).replace("星期", "周"));
            v.setWeekNo(DateUtils.getWeek(day));
            v.setShowToday(DateUtils.sameDate(day, new Date()));
            v.setShowOut(DateUtils.compareDateToBeginLtEnd(day, new Date()));
            v.setStudyFinish((float) countDayCurriculumEvent(day, true));
            v.setStudyTotal((float) countDayCurriculumEvent(day, false));
            v.setGoalFinish((float) countDayGoalEvent(day, true));
            v.setGoalTotal((float) countDayGoalEvent(day, false));
            v.setHabitFinish((float) countDayHabitEvent(day, true));
            v.setHabitTotal((float) countDayHabitEvent(day, false));

            result.add(v);
        }

        return result;
    }


    private int countDayHabitEvent(Date selectDay, boolean onlyFinish) {
        QueryBuilder<HabitEvent> q = getHabitEventDao().queryBuilder();
        q.where(HabitEventDao.Properties.ScheduleDt.ge(DateUtils.setStartTime(selectDay)));
        q.where(HabitEventDao.Properties.ScheduleDt.le(DateUtils.setEndTime(selectDay)));
        if (onlyFinish) {
            q.where(HabitEventDao.Properties.Finish.eq(true));
        }
        return (int) q.count();
    }

    private int countDayGoalEvent(Date selectDay, boolean onlyFinish) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.gt(AppStatus.EventType.LINE_GOALS));
        q.where(EventDao.Properties.EventType.lt(AppStatus.EventType.LINE_GOALS + 10));
        q.where(EventDao.Properties.ScheduleDt.ge(DateUtils.setStartTime(selectDay)));
        q.where(EventDao.Properties.ScheduleDt.le(DateUtils.setEndTime(selectDay)));
        q.where(EventDao.Properties.JoinSchedule.eq(true));
        if (onlyFinish) {
            q.where(EventDao.Properties.Finish.eq(true));
        }
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return (int) q.count();
    }

    private int countDayCurriculumEvent(Date selectDay, boolean onlyFinish) {
        QueryBuilder<Event> q = getEventDao().queryBuilder();
        q.where(EventDao.Properties.EventType.gt(AppStatus.EventType.LINE_STUDY));
        q.where(EventDao.Properties.EventType.lt(AppStatus.EventType.LINE_STUDY + 10));
        q.where(EventDao.Properties.ScheduleDt.ge(DateUtils.setStartTime(selectDay)));
        q.where(EventDao.Properties.ScheduleDt.le(DateUtils.setEndTime(selectDay)));
        q.where(EventDao.Properties.JoinSchedule.eq(true));
        if (onlyFinish) {
            q.where(EventDao.Properties.Finish.eq(true));
        }
        q.where(EventDao.Properties.Status.eq(AppStatus.Status.NORMAL));
        return (int) q.count();
    }

    public Event updateEventTitle(Long id, String title) {
        Event e = getEventDao().load(id);
        if (e == null) {
            return null;
        }
        e.setTitle(title);
        e.setUpdatedDt(new Date());
        getEventDao().save(e);

        // 查找关联的任务信息并修改
        Task t = ServiceFactory.getInstance().getEventService().findEventTask(id);
        if (t != null) {
            t.setTitle(title);
            t.setUpdatedDt(e.getUpdatedDt());
            getTaskDao().save(t);
        }
        return e;
    }
}

